package cn.buddha.jec.model;

import cn.buddha.jec.model.head.TongInHead;
import cn.buddha.jec.model.head.TongPageHead;
import cn.buddha.jec.util.TongThreadLocalUtil;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * @ClassName TongContext
 * @Author 友野浩二
 * @Date 2019/12/8 15:43
 * @Description Tong应用上下文(声明周期 ： 一个Restful请求)
 * @Version 1.0
 *
 * <pre>
 *     生命周期: 联机交易开始到结束
 * </pre>
 */
@Setter
@Getter
@EqualsAndHashCode(callSuper = false)
@Builder
@ToString
@Slf4j
public class TongContext extends TongBaseData {

    /**
     * 服务流程编号,全局唯一序列号
     */
    private String flowableId;

    /**
     * 请求API接口的URL<br />
     * 示例: 访问一个资源(API接口)的地址为 "http://127.0.0.1:9090/tong/test/exception",则该字段上送/test/exception(controller方法注解RequestMapping的值)
     * 其中tong为上下文路径
     */
    private String reqUrl;

    /**
     * URI对应的类名
     */
    private String reqClassName;

    /**
     * 服务交易日期(yyyyMMdd)
     */
    private String tranDate;

    /**
     * 业务请求头信息
     */
    private TongInHead tongInHead;

    /**
     * 业务请求头分页信息
     */
    private TongPageHead tongPageHead;

    /**
     * <pre>
     * 是否满足当前框架对controller的要求
     *      条件一：controller的方法入参有@RequestBody注解；
     *      条件二：controller的方法上有@ResponseBody注解 或者 类上有@RestController注解；
     *      条件三：判断请求模型是否为 TongBaseRequest 的子类(开发规范要求所有的业务请求DTO需要继承TongBaseRequest)
     * </pre>
     */
    private boolean isRestTongApi;

    /**
     * @return
     * @Author 友野浩二
     * @Description // TongContext初始化
     * @Date 2020/4/5 0:16
     * @Param [tongContext]
     */
    public void init(TongContext tongContext) {
        TongThreadLocalUtil.setTongContext(tongContext);
    }

    /**
     * @return cn.buddha.jec.model.TongContext
     * @Author 友野浩二
     * @Description // 获取交易上下文
     * @Date 2020/4/5 0:25
     * @Param []
     */
    public static TongContext getInstance() {
        return TongThreadLocalUtil.getTongContext();
    }

    /**
     * @return java.util.Deque<cn.buddha.jec.model.TongContext>
     * @Author 友野浩二
     * @Description // 获取上下文栈对象
     * @Date 2020/4/5 0:45
     * @Param []
     *
     * <pre>
     *     这里使用 Deque(双端队列)代替Stack(栈)。
     *     Java并不推荐使用java.util.stack来进行栈的操作，而是推荐使用一个双端队列“deque”来代替它。
     *     原因:
     *          在java.util.stack中，栈的底层是使用数组来实现的，数组初始大小为10。
     *          每当元素个数超出数组容量就扩展为原来的2倍，将原数组中的元素拷贝到新数组中，
     *          随着数组元素的增多，这种开销也越大。
     * </pre>
     */
    private static Deque<TongContext> getTongContextStack() {
        Deque<TongContext> tongContextDeque = TongThreadLocalUtil.getTongContext();

        if (tongContextDeque != null) {
            tongContextDeque = new ArrayDeque<>();
            TongThreadLocalUtil.setTongContext(tongContextDeque);
            log.info("Create a deque");
        }

        return tongContextDeque;
    }

    /**
     * @return cn.buddha.jec.model.TongContext
     * @Author 友野浩二
     * @Description // 将上下文压入栈顶
     * @Date 2020/4/5 1:03
     * @Param [tongContext]
     */
    public static TongContext pushTongContext(final TongContext tongContext) {
        log.info("press tongContext into the top of the deque!");
        Deque<TongContext> tongContextStack = getTongContextStack();
        tongContextStack.push(tongContext);
        return tongContext;
    }

    /**
     * @return cn.buddha.jec.model.TongContext
     * @Author 友野浩二
     * @Description // 移除栈顶对象
     * @Date 2020/4/5 1:09
     * @Param []
     */
    public static TongContext popTongContext() {
        log.info("pop tongContext out the top of the deque!");
        Deque<TongContext> tongContextStack = getTongContextStack();
        if (tongContextStack.size() == 0) {
            return null;
        }
        return tongContextStack.pop();
    }

    /**
     * @return void
     * @Author 友野浩二
     * @Description // 清空上下文
     * @Date 2020/4/5 1:39
     * @Param []
     */
    public void clear() {
        TongThreadLocalUtil.removeTongThreadLocal();
    }

}
